package brouter.compiler;

import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;

import brouter.annotation.BRouter;
import brouter.annotation.bean.RouterBean;
import brouter.compiler.utils.Config;
import brouter.compiler.utils.ProcessorUtil;

@AutoService(Processor.class)
@SupportedAnnotationTypes({"brouter.annotation.BRouter"}) // 注解
@SupportedSourceVersion(SourceVersion.RELEASE_8)

public class BRouterProcessor extends AbstractProcessor {
    // 文件生成器，就是最终要生成的文件(类，资源等)是需要Filer来完成的
    private Filer filer;
    // Message用来打印 日志相关信息
    private Messager messager;
    // 操作Element的工具类（类，方法，成员，其实都是Element）
    private Elements elementUtils;

    private String moduleName;
    private String aptPackage;
    private Types typeUtils;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        elementUtils = processingEnvironment.getElementUtils();
        filer = processingEnvironment.getFiler();
        messager = processingEnvironment.getMessager();
        typeUtils = processingEnvironment.getTypeUtils();

        // 获取Gradle的传参
        Map<String, String> options = processingEnvironment.getOptions();
        moduleName = options.get(Config.MODULE_NAME);
        aptPackage = options.get(Config.APT_PACKAGE);
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        messager.printMessage(Diagnostic.Kind.NOTE, ">>>>>>>>>>> process module=" + moduleName + ", aptPackage=" + aptPackage);
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BRouter.class);
        Map<String, Map<String, RouterBean>> allPathMap = getAllPathMap(elements);
        if (elements.size() == 0) {
            return false;
        }


        /**
         * 生成类模板
         * public class BRouterPathImpl implements BRouterPath {
         *     @Override
         *     public Map<String, Map<String, RouterBean>> getPathMap() {
         *         Map<String, Map<String, RouterBean>> groupMap = new HashMap<>();
         *         Map<String, RouterBean> pathMap = new HashMap<>();
         *         pathMap.put("/login/LoginActivity", RouterBean.create("/login/LoginActivity", "login", LoginActivity.class, RouterBean.TypeEnum.ACTIVITY));
         *         groupMap.put("login", pathMap);
         *         return groupMap;
         *     }
         * }
         */
        // 1. 方法
        TypeName returnType = ParameterizedTypeName.get(
                ClassName.get(Map.class),
                ClassName.get(String.class),
                ParameterizedTypeName.get(ClassName.get(Map.class), ClassName.get(String.class), ClassName.get(RouterBean.class))
        );
        MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("getPathMap")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Override.class)
                .returns(returnType);
        // 方法体 {
        // Map<String, Map<String, RouterBean>> groupMap = new HashMap<>();
        methodBuilder.addStatement("$T<$T,$T<$T,$T>> $N = new $T<>()",
                ClassName.get(Map.class),
                ClassName.get(String.class),
                ClassName.get(Map.class),
                ClassName.get(String.class),
                ClassName.get(RouterBean.class),
                "groupMap",
                ClassName.get(HashMap.class)
        );
        for (Map.Entry<String, Map<String, RouterBean>> entry : allPathMap.entrySet()) {
            // Map<String, RouterBean> pathMap = new HashMap<>();
            methodBuilder.addStatement("$T<$T,$T> $N = new $T<>()",
                    ClassName.get(Map.class),
                    ClassName.get(String.class),
                    ClassName.get(RouterBean.class),
                    "pathMap",
                    ClassName.get(HashMap.class)
            );
            for (Map.Entry<String, RouterBean> routerBeanEntry : entry.getValue().entrySet()) {
                RouterBean bean = routerBeanEntry.getValue();
                // pathMap.put("/login/LoginActivity", RouterBean.create("/login/LoginActivity", "login", LoginActivity.class, RouterBean.TypeEnum.ACTIVITY));
                methodBuilder.addStatement("$N.put($S,$T.create($S,$S,$T.class,$T.$L))", // $L 表示枚举值
                        "pathMap",
                        bean.getPath(),
                        ClassName.get(RouterBean.class),
                        bean.getPath(),
                        bean.getGroup(),
                        ClassName.get((TypeElement) bean.getElement()),
                        ClassName.get(RouterBean.TypeEnum.class), // 类型
                        bean.getType()// 枚举值
                );
            }
            // groupMap.put("login", pathMap);
            methodBuilder.addStatement("$N.put($S,$N)",
                    "groupMap",
                    entry.getKey(),
                    "pathMap"
            );
        }
        // return groupMap;
        methodBuilder.addStatement("return $N", "groupMap");
        // }方法体
        // 2. 类
        String finalClassName = Config.GROUP_FILE_NAME + moduleName;
        TypeElement pathType = elementUtils.getTypeElement(Config.BROUTER_API_PATH);
        TypeSpec typeSpec = TypeSpec.classBuilder(finalClassName)
                .addModifiers(Modifier.PUBLIC)
                .addSuperinterface(ClassName.get(pathType))
                .addMethod(methodBuilder.build())
                .build();
        // 3. 包
        JavaFile javaFile = JavaFile.builder(aptPackage, typeSpec)
                .build();

        try {
            javaFile.writeTo(filer);
        } catch (IOException e) {
            e.printStackTrace();
        }

        createPathFile(allPathMap);

        return true;
    }

    private void createPathFile(Map<String, Map<String, RouterBean>> allPathMap) {
        // 每个组为一个类，类静态成员对应Path
        for (Map.Entry<String, Map<String, RouterBean>> entry : allPathMap.entrySet()) {
            String group = entry.getKey();
            Map<String, RouterBean> paths = entry.getValue();
            String finalClassName = "BRouterPath" + ProcessorUtil.title(group);
            TypeSpec.Builder rootBuilder = TypeSpec.classBuilder(finalClassName)
                    .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
            for (Map.Entry<String, RouterBean> pathEntry : paths.entrySet()) {
                String path = pathEntry.getKey();
                RouterBean routerBean = pathEntry.getValue();
                // public static String MainActivity = "/app/MainActivity";
                FieldSpec fieldSpec = FieldSpec.builder(ClassName.get(String.class), routerBean.getElement().getSimpleName().toString(), Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
                        .initializer("$S", path)
                        .build();
                rootBuilder.addField(fieldSpec);
            }
            JavaFile javaFile = JavaFile.builder("com.breeze.common.router", rootBuilder.build())
                    .build();
            try {
                File file = new File("common/src/main/java/");// 相对根工程的路径
                javaFile.writeTo(file);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


    private Map<String, Map<String, RouterBean>> getAllPathMap(Set<? extends Element> elements) {
        Map<String, Map<String, RouterBean>> allPathMap = new HashMap<>();
        for (Element element : elements) {
            BRouter bRouter = element.getAnnotation(BRouter.class);
            RouterBean routerBean = new RouterBean.Builder()
                    .addPath(bRouter.path())
                    .addGroup(bRouter.group())
                    .addElement(element)
                    .build();

            TypeMirror activityMirror = elementUtils.getTypeElement("android.app.Activity").asType();
            TypeMirror callMirror = elementUtils.getTypeElement("com.breeze.base.Call").asType();
            TypeMirror typeMirror = element.asType();

            // RouterBean 类型处理
            if (typeUtils.isSubtype(typeMirror, activityMirror)) { // Activity上的注解
                routerBean.setType(RouterBean.TypeEnum.ACTIVITY);
            } else if (typeUtils.isSubtype(typeMirror, callMirror)) {
                routerBean.setType(RouterBean.TypeEnum.CALL);
            } else {
                messager.printMessage(Diagnostic.Kind.ERROR, "@BRouter注解不支持的类型");
            }
            // path合法性校验
            if (!checkRouterPath(routerBean)) {
                messager.printMessage(Diagnostic.Kind.ERROR, "@BRouter注解未按规范配置，如：/app/MainActivity");
            }
            // 如果从allPathMap中找不到key为：bean.getGroup()的数据，就新建pathMap集合再添加进Map
            Map<String, RouterBean> pathMap = allPathMap.get(routerBean.getGroup());
            if (ProcessorUtil.isEmpty(pathMap)) {
                pathMap = new HashMap<>();
                pathMap.put(routerBean.getPath(), routerBean);
                allPathMap.put(routerBean.getGroup(), pathMap);
            } else {
                pathMap.put(routerBean.getPath(), routerBean);
            }
        }
        return allPathMap;
    }

    /**
     * 校验@ARouter注解的值，如果group未填写就从必填项path中截取数据
     *
     * @param bean 路由详细信息，最终实体封装类
     */
    private boolean checkRouterPath(RouterBean bean) {
        String path = bean.getPath();
        String group = bean.getGroup();
        if (!ProcessorUtil.isEmpty(group) && group.equals(moduleName)) {
            // 架构师定义规范，让开发者遵循
            messager.printMessage(Diagnostic.Kind.ERROR, "@BRouter注解中的group值必须和子模块名一致！");
            return false;
        }
        if (ProcessorUtil.isEmpty(group)) {
            String parseGroup = path.substring(1, path.indexOf("/", 1));
            bean.setGroup(parseGroup);
        }
        return true;
    }
}
